﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace DarwinIsDead {

    class Player : Entity {
        private const int NUM_FINCHES = 30;
        private SpriteFont font;

        private float hurtTime;
        private float hurtCounter;
        private float hurtMagnitude;
        private float hurtDirection;
        private float hurtFrequency;
        private Vector2 hurtShake;
        
        private List<Weapon> weapons;
        private int currentWeapon;
        private bool weaponSwitched;

        // Spirte animation stuff
        private Texture2D texture;
        private List<Rectangle> frames;
        private int currentFrame;
        private float timePerFrame;
        private float frameCounter;
        private bool flipHorz;

        public bool Alive { get; set; }

        public delegate void DeathEvent();
        public DeathEvent OnDeath;

        private List<Finch> finches;

        public Player(World world, float x, float y) :
        base(world, x, y, 32f, 32f, true) {
            font = World.Game.Content.Load<SpriteFont>("Fonts\\Courier New");
            Health = MaxHealth = 100;

            hurtTime = 0.25f;
            hurtCounter = 0f;
            hurtMagnitude = 0f;
            hurtDirection = 0f;
            hurtFrequency = (float)(6.0 * Math.PI);   // Shake 1.5 cycles per 0.5 seconds
            hurtShake = new Vector2();

            weapons = new List<Weapon>();
            weapons.Add(new Weapons.Musket(World, true));
            weapons.Add(new Weapons.Pistol(World, true));
            weapons.Add(new Weapons.Gatling(World, true));
            currentWeapon = 0;
            weaponSwitched = false;

            texture = World.Game.Content.Load<Texture2D>("Sprites\\Darwin\\sprDarwin");
            frames = new List<Rectangle>();
            frames.Add(new Rectangle(0, 0, 18, 32));
            frames.Add(new Rectangle(18, 0, 18, 32));
            frames.Add(new Rectangle(0, 0, 18, 32));
            frames.Add(new Rectangle(36, 0, 18, 32));
            currentFrame = 0;
            timePerFrame = 0.15f;
            frameCounter = 0f;
            flipHorz = false;

            Alive = true;

            Random rng = new Random();
            finches = new List<Finch>();
            for (int i = 0; i < NUM_FINCHES; i++) {
                Finch finch = new Finch(World);
                finch.Color = new Color(rng.Next(255),
                                        rng.Next(255),
                                        rng.Next(255));
                finch.Rise = (float)rng.NextDouble() * 1000f;
                finch.Speed = (float)(rng.NextDouble() - 0.5) * 1000f;
                finches.Add(finch);
                World.AddEntity(finch);
            }
        }

        public override void Dispose() {
            for (int i = 0; i < weapons.Count; i++) {
                weapons[i].Dispose();
            }

            for (int i = 0; i < finches.Count; i++) {
                World.RemoveEntity(finches[i]);
            }
        }

        public int Health { get; set; }

        public int MaxHealth { get; set; }

        public override void Collision(Entity other, Rectangle overlap) {
            if (other != null && other is Bullet) {
                Bullet bullet = (Bullet)other;
                if (!bullet.Friendly) {
                    Health -= bullet.Damage;

                    // Get angle from center
                    float ox = overlap.Left - HitBox.Left;
                    float oy = overlap.Top - HitBox.Top;

                    hurtDirection = (float)Math.Atan2(oy, ox);
                    hurtCounter = hurtTime;
                    hurtMagnitude = 20f * bullet.Damage;
                }
            }


        }

        public override void Update(GameTime gameTime) {
            if (!Alive) {
                return;
            }

            float dt = gameTime.ElapsedGameTime.Milliseconds / 1000.0f;

            // Movement
            KeyboardState keyState = Keyboard.GetState();
            MouseState mouseState = Mouse.GetState();

            int vert = (keyState.IsKeyDown(Keys.S) ? 1: 0) -
                (keyState.IsKeyDown(Keys.W) ? 1 : 0);
            int horz = (keyState.IsKeyDown(Keys.D) ? 1 : 0) -
                (keyState.IsKeyDown(Keys.A) ? 1 : 0);

            float dx = dt * 100.0f * horz;
            float dy = dt * 100.0f * vert;

            // Animate if moving
            if (vert != 0 || horz != 0) {
                frameCounter += dt;
                if (frameCounter > timePerFrame) {
                    currentFrame = (currentFrame + 1) % frames.Count;
                    frameCounter = 0f;
                }
            } else {
                frameCounter = 0f;
                currentFrame = 0;
            }

            // Move
            Move(dx, dy);

            float ww = (World.Background.MAP.Width - 1) * World.Background.MAP.TileWidth;
            float wh = (World.Background.MAP.Height - 1) * World.Background.MAP.TileHeight;

            if (Position.X < 0f) {
                SetPosition(0f, Position.Y);
            } else if (Position.X > ww) {
                SetPosition(ww, Position.Y);
            }

            if (Position.Y < 0f) {
                SetPosition(Position.X, 0f);
            } else if (Position.Y > wh) {
                SetPosition(Position.X, wh);
            }

            // Mouse World.Camera movement
            World.Camera.Position = Position;

            Vector2 mousePos = new Vector2(mouseState.X, mouseState.Y);
            int vpw = World.Game.GraphicsDevice.Viewport.Width;
            int vph = World.Game.GraphicsDevice.Viewport.Height;

            if (mousePos.X < 0.0f) {
                mousePos.X = 0.0f;
            } else if (mousePos.X > vpw) {
                mousePos.X = vpw;
            }

            if (mousePos.Y < 0.0f) {
                mousePos.Y = 0.0f;
            } else if (mousePos.Y > vph) {
                mousePos.Y = vph;
            }

            float mx = (mousePos.X - (Position.X - (World.Camera.Position.X - vpw / 2.0f)));
            float my = (mousePos.Y - (Position.Y - (World.Camera.Position.Y - vph / 2.0f)));

            float direction = (float)Math.Atan2(my, mx);

            // Flip sprite if facing the correct directions
            if (mx > 0f) {
                flipHorz = false;
            } else {
                flipHorz = true;
            }

            if (hurtCounter > 0) {
                hurtCounter -= dt;
                double shake = hurtMagnitude * (hurtCounter / hurtTime) *
                    (float)Math.Sin(hurtFrequency * (hurtTime - hurtCounter));
                hurtShake.X = (float)(Math.Cos(hurtDirection) * shake);
                hurtShake.Y = (float)(Math.Sin(hurtDirection) * shake);
            }

            World.Camera.Move(0.5f * mx + hurtShake.X, 0.5f * my + hurtShake.Y);

            World.CheckCollisions(this);

            Depth = HitBox.Y + (HitBox.Width / 2);

            // Weapon switch
            if (!weaponSwitched && keyState.IsKeyDown(Keys.Q)) {
                currentWeapon = (currentWeapon + 1) % weapons.Count;
                weaponSwitched = true;
            }

            if (keyState.IsKeyUp(Keys.Q)) {
                weaponSwitched = false;
            }

            // I shoot my gun and C# be dancing
            Weapon weapon = weapons[currentWeapon];
            weapon.Direction = direction;
            weapon.Position = Position;

            weapon.Update(gameTime);

            // Some things just arent worth dying for
            if (Health <= 0 || keyState.IsKeyDown(Keys.K)) {
                if (OnDeath != null) {
                    OnDeath();
                }
                Death();
            }            
        }

        private void Death() {
            for (int i = 0; i < finches.Count; i++) {
                finches[i].Spawn(Position);
            }

            Alive = false;
            Solid = false;
        }


        public override void Draw(SpriteBatch spriteBatch) { 
            //spriteBatch.Draw(sprite, Position, null, Color.White, 0, Vector2.Zero, new Vector2(0.4f, 0.4f), SpriteEffects.None,
            //    0f);
            if (Alive) {
                spriteBatch.Draw(texture, new Vector2(Position.X, Position.Y - 16f), frames[currentFrame],
                    Color.White, 0f, Vector2.Zero, 2f,
                    flipHorz ? SpriteEffects.FlipHorizontally : SpriteEffects.None,
                    0f);
                weapons[currentWeapon].Draw(spriteBatch);
            }
        }

        public override void DrawUI(SpriteBatch spriteBatch) {
            if (Alive) {
                spriteBatch.DrawString(font, "Health: " + Health, Vector2.Zero, Color.White);
                weapons[currentWeapon].DrawUI(spriteBatch);
            }
        }
    }
}
